package com.android.kotlincoroutinemvvmdemo.shader
import android.opengl.GLES30
import android.opengl.GLSurfaceView
import android.opengl.Matrix
import java.nio.ByteBuffer
import java.nio.ByteOrder
import java.nio.FloatBuffer
import javax.microedition.khronos.egl.EGLConfig
import javax.microedition.khronos.opengles.GL10

class TriangleRenderer : GLSurfaceView.Renderer {

    private lateinit var mTriangle: Triangle

    private val vPMatrix = FloatArray(16)
    private val projectionMatrix = FloatArray(16)
    private val viewMatrix = FloatArray(16)

    override fun onSurfaceCreated(gl: GL10?, config: EGLConfig?) {
        GLES30.glClearColor(0.0f, 0.0f, 0.0f, 1.0f)
        mTriangle = Triangle()
    }

    override fun onSurfaceChanged(gl: GL10?, width: Int, height: Int) {
        GLES30.glViewport(0, 0, width, height)
        //透视投影
        val ratio: Float = width.toFloat() / height.toFloat()
        Matrix.frustumM(projectionMatrix, 0, -ratio, ratio, -1f, 1f, 3f, 7f)
    }

    override fun onDrawFrame(gl: GL10?) {
        GLES30.glClear(GLES30.GL_COLOR_BUFFER_BIT)
        //设置相机位置
        Matrix.setLookAtM(viewMatrix, 0, 0f, 0f, -3f, 0f, 0f, 0f, 0f, 1.0f, 0.0f)
        //Calculate the projection and view transformation(计算转换)
        Matrix.multiplyMM(vPMatrix, 0, projectionMatrix, 0, viewMatrix, 0)

        mTriangle.draw(vPMatrix)
    }

    class Triangle {
        private var mProgram: Int

        private var positionHandle: Int = 0
        private var mColorHandle: Int = 0
        private var vPMatrixHandle: Int = 0

        private val vertexShaderCode =
                    "uniform mat4 uMVPMatrix;" +
                    "attribute vec4 vPosition;" +
                    "void main() {" +
                    "  gl_Position = uMVPMatrix * vPosition;" +
                    "}"

        private val fragmentShaderCode =
                    "precision mediump float;" +
                    "uniform vec4 vColor;" +
                    "void main() {" +
                    "  gl_FragColor = vColor;" +
                    "}"

        private val color = floatArrayOf(
            1.0f, 0.7f, 0.25f, 1.0f,
            1.0f, 0.3f, 0.65f, 1.0f,
            1.0f, 0.89f, 0.205f, 1.0f,
        )

        //每一行有几个顶点
        private val COORDS_PER_VERTEX = 3

        //顶点坐标
        private var triangleCoords = floatArrayOf(
            -0.5f, 0.5f, 0.0f,
            -0.5f, -0.3f, 0.0f,
            0.5f, 0.4f, 0.0f
        )

//        private var triangleCoords = floatArrayOf(
//            0.0f, 0.0f, -0.5f,
//            0.0f, 0.0f, 0.0f,
//            0.5f, 0.0f, 0.0f
//        )

        private val vertexCount: Int = triangleCoords.size / COORDS_PER_VERTEX
        private val vertexStride: Int = COORDS_PER_VERTEX * 4 //4 bytes per vertex

        //直接获取顶点缓存
        private var vertexBuffer: FloatBuffer =
            ByteBuffer.allocateDirect(triangleCoords.size * 4).run {
                order(ByteOrder.nativeOrder())
                asFloatBuffer().apply {
                    put(triangleCoords)
                    position(0)
                }
            }

        private var vertexColorBuffer: FloatBuffer =
            ByteBuffer.allocateDirect(color.size * 4).run {
                order(ByteOrder.nativeOrder())
                asFloatBuffer().apply {
                    put(color)
                    position(0)
                }
            }

        init {
            val vertexShader: Int = loadShader(GLES30.GL_VERTEX_SHADER, vertexShaderCode)
            val fragmentShader: Int = loadShader(GLES30.GL_FRAGMENT_SHADER, fragmentShaderCode)
            //create empty OpenGL ES Program(加载程序)
            mProgram = GLES30.glCreateProgram().also {
                //add the vertex shader to program
                GLES30.glAttachShader(it, vertexShader)
                //add the fragment shader to program
                GLES30.glAttachShader(it, fragmentShader)
                //creates OpenGL ES program executables
                GLES30.glLinkProgram(it)
            }
        }

        fun draw(mvpMatrix: FloatArray) {
            //Add program to OpenGL ES environment(添加程序到OpenGL ES环境)
            GLES30.glUseProgram(mProgram)

            //get handle to shape's transformation matrix(获取形状转换句柄)
            vPMatrixHandle = GLES30.glGetUniformLocation(mProgram, "uMVPMatrix")

            //get handle to vertex shader's vPosition member(获取形状位置句柄)
            positionHandle = GLES30.glGetAttribLocation(mProgram, "vPosition").also {
                //Enable a handle to the triangle vertices(启用顶点的句柄)
                GLES30.glEnableVertexAttribArray(it)
                //Prepare the triangle coordinate data(加载位置数据)
                GLES30.glVertexAttribPointer(
                    it,
                    COORDS_PER_VERTEX,
                    GLES30.GL_FLOAT,
                    false,
                    vertexStride,
                    vertexBuffer
                )

                //get handle to fragment shader's vColor member(获取颜色句柄)
                mColorHandle = GLES30.glGetUniformLocation(mProgram, "vColor").also { colorHandle ->
                    //Set color for drawing the triangle(着色)
                    GLES30.glUniform4fv(colorHandle, 1, color, 0)
                    //GLES30.glUniform4fv(colorHandle, 3, vertexColorBuffer)
                }

                //Pass the projection and view transformation to the shader(转换矩阵)
                GLES30.glUniformMatrix4fv(vPMatrixHandle, 1, false, mvpMatrix, 0)

                //Draw the triangle(绘制三角形)
                GLES30.glDrawArrays(GLES30.GL_TRIANGLES, 0, vertexCount)

                //Disable vertex array(禁用顶点的句柄)
                GLES30.glDisableVertexAttribArray(it)
            }
        }
    }

    companion object {
        //加载渲染层
        fun loadShader(type: Int, shaderCode: String): Int {
            //create a vertex shader type (GLES30.GL_VERTEX_SHADER)----顶点图层
            //or a fragment shader type (GLES30.GL_FRAGMENT_SHADER)----着色图层
            return GLES30.glCreateShader(type).also { shader ->
                //add the source code to the shader and compile it
                GLES30.glShaderSource(shader, shaderCode)
                GLES30.glCompileShader(shader)
            }
        }
    }

}